
#include "Class.h"

#include "bootstrap.h"
#include "Class.h"
#include "PrimitiveMethod.h"
#include "InterpretClosure.h"
#include "util.h"

Class* stringCls(Image* image){
    Class* stringCls=createClass(image,"String");
    stringCls->setFinal(true);
    image->clsString=stringCls;
    //length of the string
    stringCls->addCustomField("data",4);
    new PrimitiveMethod(image,"length",stringCls,0,&primitive_string_length);
    new PrimitiveMethod(image,"at:",stringCls,1,&primitive_string_at);
}
Class* consoleCls(Image* image){
    Class* consoleCls=createClass(image,"Console");
    new PrimitiveMethod(image,"string:",consoleCls,1,&primitive_con_string);
    new PrimitiveMethod(image,"nl",consoleCls,0,&primitive_con_newline);
    
    Object* singleton=consoleCls->newInstance();
    //printf("Immediate test %s.\n",((Class*)singleton->getClass())->getName().c_str());

    image->addObject("Con",singleton);
}
Class* integerCls(Image* image){
   Class* intCls=createClass(image,"Integer");
   image->clsInteger=intCls;

   intCls->addCustomField("data",4);
   new PrimitiveMethod(image,"+:",intCls,1,primitive_mathop_add);
   new PrimitiveMethod(image,"-:",intCls,1,primitive_mathop_substract);
   new PrimitiveMethod(image,"*:",intCls,1,primitive_mathop_multiply);
   new PrimitiveMethod(image,"/:",intCls,1,primitive_mathop_divide);
   new PrimitiveMethod(image,"%:",intCls,1,primitive_mathop_mod);
   new PrimitiveMethod(image,"=:",intCls,1,primitive_test_equal);
   new PrimitiveMethod(image,"!=:",intCls,1,primitive_test_notEqual);

   new PrimitiveMethod(image,">:",intCls,1,&primitive_test_greater);
   new PrimitiveMethod(image,">=:",intCls,1,&primitive_test_greater_or_equal);
   new PrimitiveMethod(image,"<:",intCls,1,&primitive_test_lesser);
   new PrimitiveMethod(image,"<=:",intCls,1,&primitive_test_lesser_or_equal);
}
Class* booleanCls(Image* image){
   Class* cls=createClass(image,"Boolean");
   cls->addCustomField("data",1);
   image->clsBoolean=cls;
   new PrimitiveMethod(image,"ifTrue:else:",cls,2,&primitive_if);
}
Class* closureCls(Image* image){
   Class* cls=createClass(image,"InterpretClosure");
   cls->addCustomField("closure_data",sizeof(InterpretClosure)-sizeof(Object));
   cls->setFinal(true);
   cls->addCustomField("data",sizeof(InterpretClosure)-sizeof(Object));
   new PrimitiveMethod(image,"value",cls,0,&primitive_closure_execute);
   new PrimitiveMethod(image,"value:",cls,1,&primitive_closure_execute);
   new PrimitiveMethod(image,"value:value:",cls,2,&primitive_closure_execute);
   new PrimitiveMethod(image,"value:value:value:",cls,3,&primitive_closure_execute);
   new PrimitiveMethod(image,"value:value:value:value:",cls,4,&primitive_closure_execute);
   image->clsIClosure=cls;
}
Object* primitive_xclass_initialize(Image* image,Object* self,int argc,Object** argv){
    self->set("providers",NULL);
    self->set("providedMethods",NULL);
}
Object* primitive_xclass_findMethod(Image* image,Object* self,int argc,Object** argv){
    string selector=inspectString(argv[0]);
    Object* methods=self->get("providedMethods");
    if(methods!=NULL){
        Object* array=methods->get("array");
        int length=*((int*)array->dataAt(ARRAY_OBJECT_LENGTH_OFFSET));
        Object** data=(Object**)array->dataAt(ARRAY_OBJECT_DATA_OFFSET);
        for(int i=0;i<length;i++){
            Method* m=(Method*)data[i];
            if(m!=NULL){
                if(m->getSelector()==selector) return m;
            }
        }
    }
    return NULL;
}
Class* xclassCls(Image* image){
    /* we may create objects normally from now on */
    /* now create XClass, because other internals use it */

    Class* xcls=(Class*)image->clsClass->newInstance();
    xcls->setName("XClass");
    xcls->setParent(image->clsClass);
    xcls->addField("providers",NULL,FIELD_ORDINARY);
    xcls->addField("providedMethods",NULL,FIELD_ORDINARY);
    new PrimitiveMethod(image,"initialize",xcls,0,&primitive_xclass_initialize);
    new PrimitiveMethod(image,"findMethod:",xcls,1,&primitive_xclass_findMethod);
    image->clsXClass=xcls;
}
Class* classCls(Image* image){
    /* initialization here is cumbersome, we need class in order to init other class */
    /* can not name it, because it needs initialization routines */
    Class* cls=new Class();
    PrimitiveMethod* m1=new PrimitiveMethod(image,"setParent:",cls,1,&primitive_class_set_parent);
    PrimitiveMethod* m2=new PrimitiveMethod(image,"initialize",cls,0,&primitive_class_initialize);
    image->clsClass=cls;
    /* manual pre-initialization */
    cls->image=image;
    /* initialize itself */
    cls->genVPTR(false);
    cls->initializeObject(cls);
    /* we can delete the old stuff now */
    delete m1; delete m2;
    /* we have to specify the methods again, they got erased */
    cls->addCustomField("class_data",sizeof(Class)-sizeof(Object));
    new PrimitiveMethod(image,"setParent:",cls,1,&primitive_class_set_parent);
    new PrimitiveMethod(image,"setName:",cls,1,&primitive_class_set_name);
    new PrimitiveMethod(image,"addField:",cls,1,&primitive_class_add_field);
    new PrimitiveMethod(image,"initialize",cls,0,&primitive_class_initialize);
    new PrimitiveMethod(image,"new",cls,0,&primitive_class_new);
    new PrimitiveMethod(image,"addMethod:",cls,1,&primitive_class_add_method);
    /* name it */
    cls->setName("Class");

    /* create object class */
    Class* objectCls=new Class();
    m1=new PrimitiveMethod(image,"initialize",objectCls,0,&primitive_do_nothing);
    image->clsObject=objectCls;
    /* another pre-initi */
    objectCls->genVPTR(false);
    /* real init */
    cls->initializeObject(objectCls);
    /* delete old stuff */
    delete m1;
    /* parentClass is pre-initialized to Object, so delete it */
    objectCls->parentClass=NULL;
    objectCls->subclasses.erase(objectCls);
    /* just complete the class */
    new PrimitiveMethod(image,"initialize",objectCls,0,&primitive_do_nothing);
    new PrimitiveMethod(image,"isNull",objectCls,0,&primitive_is_null);
    objectCls->addField("_vptr",NULL,FIELD_ORDINARY);

    objectCls->setName("Object");

    cls->setParent(objectCls);
    xclassCls(image);

    /* create Method class */
    /* TODO: redefine methods once more to initialize their vptr. */
    Class* method=createClass(image,"Method");
    new PrimitiveMethod(image,"getSelector",method,0,&primitive_method_get_selector);
    image->clsMethod=method;

    /* define undefined object */
    Class* nullCls=createClass(image,"NullObject");
    image->clsNull=nullCls;
}
void rangeCls(Image* image){
    Class* mcls=createClass(image,"RangeClass");
    mcls->setParent(image->clsClass);
    
    Class* cls=(Class*)mcls->newInstance();
    cls->setName("Range");
    cls->addField("from",NULL,FIELD_ORDINARY);
    cls->addField("to",NULL,FIELD_ORDINARY);
    new PrimitiveMethod(image,"do:",cls,1,&primitive_range_do);
    image->clsRange=cls;
}
void arrayCls(Image* image){
    Class* mcls=createClass(image,"ArrayClass");
    new PrimitiveMethod(image,"new:",mcls,1,&primitive_array_new);
    mcls->setParent(image->clsClass);
            
    Class* cls=(Class*)mcls->newInstance();
    cls->setName("Array");
    new PrimitiveMethod(image,"at:",cls,1,&primitive_array_at);
    new PrimitiveMethod(image,"at:set:",cls,2,&primitive_array_at_set);
    new PrimitiveMethod(image,"length",cls,0,&primitive_array_size);
    new PrimitiveMethod(image,"do:",cls,1,&primitive_array_do);
    new PrimitiveMethod(image,"resize:",cls,1,&primitive_array_resize);
    new PrimitiveMethod(image,"toStringAsByteArrayFrom:to:",cls,2,&primitive_array_to_string_as_byte_array);
    cls->addCustomField("array_length",sizeof(size_t));
    cls->setFinal(true);
    image->clsArray=cls;
}
void imageCls(Image* image){
    Class* cls=createClass(image,"MtalkImage");
    new PrimitiveMethod(image,"addNamed:as:",cls,2,&primitive_image_add_object);
    cls->initializeObject(image);
}
void bootstrap(Image* image){
    classCls(image);
    imageCls(image);
    closureCls(image);
    booleanCls(image);
    integerCls(image);
    rangeCls(image);
    arrayCls(image);
    stringCls(image);
    consoleCls(image);
}